/*
 ===========================================================================
 Copyright (c) 2012 Three Pillar Global Inc. http://threepillarglobal.com

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 ===========================================================================
 */

package com.common.utils.social;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.PopupWindow;

import org.brickred.socialauth.Album;
import org.brickred.socialauth.AuthProvider;
import org.brickred.socialauth.Career;
import org.brickred.socialauth.Contact;
import org.brickred.socialauth.Feed;
import org.brickred.socialauth.Profile;
import org.brickred.socialauth.SocialAuthConfig;
import org.brickred.socialauth.SocialAuthManager;
import org.brickred.socialauth.exception.SocialAuthException;
import org.brickred.socialauth.plugin.AlbumsPlugin;
import org.brickred.socialauth.plugin.CareerPlugin;
import org.brickred.socialauth.plugin.FeedPlugin;
import org.brickred.socialauth.util.AccessGrant;
import org.brickred.socialauth.util.Constants;
import org.brickred.socialauth.util.MethodType;
import org.brickred.socialauth.util.OAuthConfig;
import org.brickred.socialauth.util.Response;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Main class of the SocialAuth Android SDK. Wraps a user interface component
 * with the SocialAuth functionality of updating status, getting user profiles,
 * contacts, upload images, get user feeds, get user albums on Facebook,
 * Twitter, LinkedIn, MySpace, Yahoo, Google FourSquare, Runkeeper, SalesForce
 * and Yammer. <br>
 * <p/>
 * Currently it can be used in four different ways. First, it can be attached
 * with a Button that user may click. Clicking will open a menu with various
 * social networks listed that the user can click on. Clicking on any network
 * opens a dialog for authentication with that social network. Once the user is
 * authenticated, you can use various methods from the AuthProvider interface to
 * update status, get profile, contacts, user feeds, album feeds and upload
 * images. <br>
 * <p/>
 * Secondly, it can be attached to a LinearLayout for creating a Bar with
 * several buttons, one for each social network. Clicking on these icons will
 * open a dialog which will authenticate the user and one the user is
 * authenticated, you can use various methods from the AuthProvider interface to
 * update status, get profile, contacts, user feeds, album feeds and upload
 * images. <br>
 * <p/>
 * Thirdly, it can be used as shareactionprovider in actionbar.. Clicking on it it will show list
 * of all providers along with other apps which are use by ACTION_SEND intent. The socialauth
 * providers will be shown is added else it will default application. For example
 * if you added socialauth facebook provider then it will show  socialauth facebook
 * provider else it will show default facebook application.<br>
 * <p/>
 * Lastly, you can just launch the authentication dialog directly from any event
 * you prefer. Examples for all of these ways is provided in the examples
 * directory of the SocialAuth Android SDK
 *
 * @author vineet.aggarwal@3pillarglobal.com
 * @author abhinav.maheswari@3pillarglobal.com
 */
public class SocialAuthAdapter {

    // Share Mail & MMS providers
    public static final String SHARE_MAIL = "share_mail";
    public static final String SHARE_MMS = "share_mms";
    // Constants
    public static final String PROVIDER = "provider";
    public static final String ACCESS_GRANT = "access_grant";
    // Facebook feed url for updating story
    private final String UPDATE_STATUS_URL = "https://graph.facebook.com/me/feed";
    // contains array of providers
    private final Provider authProviders[];
    private final int authProviderLogos[];
    private final Map<String, OAuthConfig> authMap;
    // Android Components
    private final Handler handler = new Handler();
    // socialAuthManager object
    private SocialAuthManager socialAuthManager;
    // dialogListener object
    private DialogListener dialogListener;
    // provides currentprovider information
    private Provider currentProvider;
    // Variables, Arrays and Maps
    private String url;
    private String storyResult;
    private int providerCount = 0;
    private Map<String, Object> tokenMap;

    /**
     * Constructor
     *
     * @param listener Listener for the adapter events
     */

    public SocialAuthAdapter(DialogListener listener) {
        authProviders = new Provider[Provider.values().length];
        authProviderLogos = new int[Provider.values().length];
        this.dialogListener = listener;
        authMap = new HashMap<String, OAuthConfig>();
    }

    /**
     * Attaches a new listener to the adapter. Define logos and providers.
     *
     * @param listener
     */
    public void setListener(DialogListener listener) {
        this.dialogListener = listener;
    }

    /**
     * Enables a provider
     *
     * @param provider Provider to be enables
     * @param logo     Image resource for the logo of the provider
     */
    public void addProvider(Provider provider, int logo) {
        authProviders[providerCount] = provider;
        authProviderLogos[providerCount] = logo;
        providerCount++;
    }

    /**
     * Adds callback URL
     *
     * @param provider Provider to be enables
     * @param callBack CallBack URL String
     */
    public void addCallBack(Provider provider, String callBack) {
        if (provider.name() == Constants.FACEBOOK || provider.name() == Constants.LINKEDIN
                || provider.name() == Constants.MYSPACE || provider.name() == Constants.YAHOO
                || provider.name() == Constants.RUNKEEPER) {
            Log.d("SocialAuthAdapter", "Callback Url not require");
        } else
            provider.setCallBackUri(callBack);
    }

    /**
     * Adds Provider Name
     *
     * @param provider Provider to be enables
     * @param name     Provider name
     */
    public void addProviderName(Provider provider, String name) {
        if (provider == Provider.GENERIC) {
            provider.setName(name);
        } else {
            Log.d("SocialAuthAdapter", "Only to set Generic Provider Name");
        }
    }

    /**
     * Returns the last authenticated provider. Please use the SocialAuth API to
     * find out about the methods available in this interface
     *
     * @return Provider object
     */
    public AuthProvider getCurrentProvider() {
        if (currentProvider != null) {
            return socialAuthManager.getProvider(currentProvider.toString());

        } else {
            return null;
        }
    }

    /**
     * Enables a button with the SocialAuth menu
     *
     * @param sharebtn The button that will be clicked by user to start sharing
     */
    public void enable(Button sharebtn) {

        Log.d("SocialAuthAdapter", "Enabling button with SocialAuth");
        final Context ctx = sharebtn.getContext();

        // Click Listener For Share Button
        sharebtn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {

                // Creating dialog to show list of all providers
                AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
                builder.setTitle("Share via");
                builder.setCancelable(true);
                builder.setIcon(android.R.drawable.ic_menu_more);

                // Getting Provider Names and Logos to display
                String[] providerNames = new String[providerCount];
                int[] providerLogos = new int[providerCount];

                for (int i = 0; i < providerCount; i++) {
                    providerNames[i] = authProviders[i].toString();
                    providerLogos[i] = authProviderLogos[i];
                }

                // Handle click events
                builder.setSingleChoiceItems(new ShareButtonAdapter(ctx, providerNames, providerLogos), 0,
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int item) {
                                if (authProviders[item].toString().startsWith("share_mail")
                                        || authProviders[item].toString().startsWith("share_mms")) {
                                    // Getting selected provider email or mms
                                    Bundle bundle = new Bundle();
                                    bundle.putString(SocialAuthAdapter.PROVIDER, authProviders[item].toString());
                                    dialogListener.onComplete(bundle);
                                } else {
                                    // Getting selected provider and starting
                                    // authentication
                                    authorize(ctx, authProviders[item]);
                                }
                                dialog.dismiss();
                            }
                        });
                final AlertDialog dialog = builder.create();
                dialog.show();
            }
        });

        // If network not available show message
        if (!Util.isNetworkAvailable(ctx)) {
            dialogListener.onError(new SocialAuthError("Please check your Internet connection", new Exception("")));
            return;
        }
    }

    /**
     * Enables a button with the SocialAuth menu
     *
     * @param actionView The button on providersthat will be clicked by user to show
     *                   list of providers
     */
    public void enable(final View actionView) {

        // Creating List to show providers
        final Context ctx = actionView.getContext();
        final ListView pList = new ListView(ctx);
        pList.setLayoutParams(new LinearLayout.LayoutParams(500, 500));
        pList.setBackgroundColor(Color.WHITE);

        // Getting provider names and logos to display
        final String[] providerNames = new String[providerCount];
        final int[] providerLogos = new int[providerCount];

        for (int i = 0; i < providerCount; i++) {
            providerNames[i] = authProviders[i].toString();
            providerLogos[i] = authProviderLogos[i];
        }

        // Query native apps
        final ArrayList<AppList> appList = Util.queryIntentActivities(ctx, providerNames, providerLogos);

        // Handle Click Events on Action Provider button
        actionView.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {

                // Populating List
                pList.setAdapter(new PopUpListAdapter(ctx, appList));
                pList.setDivider(new ColorDrawable(0x99474747));

                // Creating Popup Window to show List
                final PopupWindow popupWindow = new PopupWindow(ctx);
                popupWindow.setFocusable(true);
                WindowManager wm = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE);
                float width = (wm.getDefaultDisplay().getWidth() * 2) / 3;
                popupWindow.setWidth((int) width);
                popupWindow.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);

                // set the list view as pop up window content
                popupWindow.setContentView(pList);
                popupWindow.showAsDropDown(actionView, 10, 10);

                // Handle Click events on list item
                pList.setOnItemClickListener(new OnItemClickListener() {
                    @Override
                    public void onItemClick(AdapterView<?> adapter, View view, int position, long arg) {

                        // Dismiss popup window
                        popupWindow.dismiss();

                        // Start provider
                        if (position > providerNames.length) {
                            ctx.startActivity(appList.get(position).intent);

                        } else {
                            authorize(ctx, authProviders[position]);
                        }
                    }
                });
            }
        });

        // If network not available show message
        if (!Util.isNetworkAvailable(ctx)) {
            dialogListener.onError(new SocialAuthError("Please check your Internet connection", new Exception("")));
            return;
        }
    }

    /**
     * Enables a LinearLayout with SocialAuth functionality
     *
     * @param linearbar The LinearLayout which is created as a bar
     */
    public void enable(LinearLayout linearbar) {
        Log.d("SocialAuthAdapter", "Enabling bar with SocialAuth");
        final Context ctx = linearbar.getContext();

        // Handles Clicking Events for Buttons
        OnClickListener viewlistener = new OnClickListener() {
            @Override
            public void onClick(View v) {
                // Getting selected provider and starting authentication
                if (authProviders[v.getId()].toString().startsWith("share_mail")
                        || authProviders[v.getId()].toString().startsWith("share_mms")) {
                    Bundle bundle = new Bundle();
                    bundle.putString(SocialAuthAdapter.PROVIDER, authProviders[v.getId()].toString());
                    dialogListener.onComplete(bundle);
                } else {
                    // Getting selected provider and starting authentication
                    authorize(ctx, authProviders[v.getId()]);
                }
            }
        };

        // Adding Buttons to Bar
        for (int i = 0; i < providerCount; i++) {
            ImageView provider = new ImageView(ctx);
            provider.setId(i);
            provider.setImageResource(authProviderLogos[i]);
            provider.setPadding(5, 5, 5, 5);
            provider.setOnClickListener(viewlistener);
            linearbar.addView(provider);
        }

        // If network not available show message
        if (!Util.isNetworkAvailable(ctx)) {
            dialogListener.onError(new SocialAuthError("Please check your Internet connection", new Exception("")));
            return;
        }
    }

    /**
     * Method to handle configuration , Use directly for CustomUI
     *
     * @param ctx      activity context
     * @param provider name of provider
     */

    public void authorize(Context ctx, Provider provider) {

        currentProvider = provider;
        Log.d("SocialAuthAdapter", "Selected provider is " + currentProvider);

        // Initialize socialauth manager if not already done
        if (socialAuthManager != null) {
            // If SocialAuthManager is not null and contains Provider Id, send
            // response to listener
            if (socialAuthManager.getConnectedProvidersIds().contains(currentProvider.toString())) {
                Log.d("SocialAuthAdapter", "Provider already connected");
                Bundle bundle = new Bundle();
                bundle.putString(SocialAuthAdapter.PROVIDER, currentProvider.toString());
                dialogListener.onComplete(bundle);
            }

            // If SocialAuthManager is not null and not contains Provider Id
            // connect provider
            else {
                connectProvider(ctx, provider);
            }

        }
        // If SocialAuthManager is null, create new socialauthmanager , load
        // configuration and connect provider
        else {
            Log.d("SocialAuthAdapter", "Loading keys and secrets from configuration");

            socialAuthManager = new SocialAuthManager();
            try {
                loadConfig(ctx);

            } catch (Exception e) {
                Log.d("SocialAuthAdapter", "Could not load configuration");
            }
            connectProvider(ctx, provider);
        }

        // If network not available show message
        if (!Util.isNetworkAvailable(ctx)) {
            dialogListener.onError(new SocialAuthError("Please check your Internet connection", new Exception("")));
            return;
        }
    }

    /**
     * Method to add user defined configuration. Please delete
     * oauth_consumers.properties before using this method.
     *
     * @param provider    name of provider
     * @param key         provider key
     * @param secret      provider secret
     * @param permissions permissions for provider
     */

    public void addConfig(Provider provider, String key, String secret, String permissions) throws Exception {
        OAuthConfig authConfig = new OAuthConfig(key, secret);
        authConfig.setId(provider.toString());
        authConfig.setCustomPermissions(permissions);
        authMap.put(provider.toString(), authConfig);
    }

    /**
     * Internal method to load config
     *
     * @param context The Android Activity context
     */

    private void loadConfig(Context ctx) throws Exception {

        SocialAuthConfig config = new SocialAuthConfig();
        Resources resources = ctx.getResources();
        AssetManager assetManager = resources.getAssets();
        InputStream inputStream = null;
        boolean fileExist = false;
        // Check oauth_consumer.properties file exist
        try {
            inputStream = assetManager.open("oauth_consumer.properties");
            fileExist = true;
        } catch (Exception e) {
            fileExist = false;
            Log.d("SocialAuthAdapter", "oauth_consumer.properties not found");
        }

        if (fileExist) {
            // Add keys from oauth_consumers file. loadConfig() method
            // is removed
            config.load(inputStream);
            socialAuthManager.setSocialAuthConfig(config);
        } else {
            // Add user keys if outh_consumers file not exists
            for (String key : authMap.keySet()) {
                config.addProviderConfig(key, authMap.get(key));
            }
            socialAuthManager.setSocialAuthConfig(config);
        }
    }

    /**
     * Internal method to handle dialog-based authentication backend for
     * authorize().
     *
     * @param context  The Android Activity that will parent the auth dialog.
     * @param provider Provider being authenticated
     */
    private void startDialogAuth(final Context context, final Provider provider) {
        CookieSyncManager.createInstance(context);

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    // Get Callback url
                    url = socialAuthManager.getAuthenticationUrl(provider.toString(), provider.getCallBackUri())
                            + "&type=user_agent&display=touch";

                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            Log.d("SocialAuthAdapter", "Loading URL : " + url);
                            String callbackUri = provider.getCallBackUri();
                            Log.d("SocialAuthAdapter", "Callback URI : " + callbackUri);
                            // start webview dialog
                            new SocialAuthDialog(context, url, provider, dialogListener, socialAuthManager).show();
                        }
                    });
                } catch (Exception e) {
                    dialogListener.onError(new SocialAuthError("URL Authentication error", e));
                }
            }
        };

        new Thread(runnable).start();
    }

    /**
     * Internal method to connect provider. The method check for access token If
     * available it connects manager with AccessGrant else create new manager
     * and open webview
     *
     * @param context  The Android Activity that will parent the auth dialog.
     * @param provider Provider being authenticated
     */

    private void connectProvider(final Context ctx, final Provider provider) {

        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(ctx);

        if (pref.contains(provider.toString() + " key")) {
            tokenMap = new HashMap<String, Object>();

            for (Map.Entry entry : pref.getAll().entrySet())
                tokenMap.put(entry.getKey().toString(), entry.getValue());

            // If Access Token is available , connect using Access Token
            try {

                HashMap<String, Object> attrMap = null;
                attrMap = new HashMap<String, Object>();

                String key = (String) tokenMap.get(provider.toString() + " key");
                String secret = (String) tokenMap.get(provider.toString() + " secret");
                String providerid = (String) tokenMap.get(provider.toString() + " providerid");

                String temp = provider.toString() + "attribute";
                for (String attr : tokenMap.keySet()) {
                    System.out.println("Attr " + attr);

                    if (attr.startsWith(temp)) {
                        int startLocation = attr.indexOf(temp) + temp.length() + 1;
                        attrMap.put(attr.substring(startLocation), tokenMap.get(attr));
                    }

                }

                for (Map.Entry entry : attrMap.entrySet()) {
                    System.out.println(entry.getKey() + ", " + entry.getValue());
                }

                // create new AccessGrant Object
                final AccessGrant accessGrant = new AccessGrant(key, secret);
                accessGrant.setProviderId(providerid);
                accessGrant.setAttributes(attrMap);

                Log.d("SocialAuthAdapter", "Loading from AccessToken");

                Runnable runnable = new Runnable() {
                    @Override
                    public void run() {
                        try {

                            // connect manager with accessGrant
                            socialAuthManager.connect(accessGrant);

                            // To check validity of Access Token
                            getCurrentProvider().getUserProfile().getValidatedId();

                            handler.post(new Runnable() {
                                @Override
                                public void run() {

                                    Bundle bundle = new Bundle();
                                    bundle.putString(SocialAuthAdapter.PROVIDER, currentProvider.toString());
                                    dialogListener.onComplete(bundle);
                                }
                            });
                        } catch (Exception e) {
                            dialogListener.onError(new SocialAuthError("Token Error", e));
                            Log.d("SocialAuthAdapter", "Starting webview for authentication for new Token");

                            // Create new SocialAuth Manager if token error
                            // occurs
                            socialAuthManager = new SocialAuthManager();
                            try {
                                loadConfig(ctx);
                            } catch (Exception e1) {
                                e1.printStackTrace();
                            }
                            startDialogAuth(ctx, currentProvider);

                        }
                    }
                };

                new Thread(runnable).start();

            } catch (Exception e) {
                dialogListener.onError(new SocialAuthError("Unknown error", e));
                e.printStackTrace();
            }
        }
        // If Access Token is not available , Open Authentication Dialog
        else {
            Log.d("SocialAuthAdapter", "Starting webview for authentication");
            startDialogAuth(ctx, currentProvider);
        }

    }

    /**
     * Sets Size of Dialog. Max value for Portrait and Landscape - 40,60
     */
    public void setDialogSize(float width, float height) {
        if (width < 0 || width > 40)
            SocialAuthDialog.width = 40;
        else
            SocialAuthDialog.width = width;

        if (height < 0 || height > 60)
            SocialAuthDialog.height = 60;
        else
            SocialAuthDialog.height = height;
    }

    /**
     * Signs out the user out of current provider
     *
     * @return Status of signing out
     */
    public boolean signOut(Context ctx, String providerName) {

        // remove cookies
        Context appContext = ctx.getApplicationContext();
        CookieSyncManager cookieSyncMngr = CookieSyncManager.createInstance(appContext);
        CookieManager cookieManager = CookieManager.getInstance();
        cookieManager.removeAllCookie();

        if (providerName != null) {

            if (socialAuthManager.getConnectedProvidersIds().contains(providerName))
                socialAuthManager.disconnectProvider(providerName);

            Editor edit = PreferenceManager.getDefaultSharedPreferences(appContext).edit();
            edit.remove(providerName + " key");
            edit.commit();

            Log.d("SocialAuthAdapter", "Disconnecting Provider");

            return true;
        } else {
            Log.d("SocialAuthAdapter", "The provider name should be same");
            return false;
        }
    }

    /**
     * Disable title of dialog.
     *
     * @param titleStatus default false , Set true to disable dialog titlebar
     */
    public void setTitleVisible(boolean titleStatus) {
        SocialAuthDialog.titleStatus = titleStatus;
    }

    /**
     * Method to update status of user
     *
     * @param message     The message to be send.
     * @param listener    socialAuth listener to get status
     * @param shareOption true - share on all providers false - share on current
     *                    provider
     */

    public void updateStatus(final String message, final SocialAuthListener<Integer> listener, final boolean shareOption) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    if (shareOption == true) {
                        final List<String> activeProviders = socialAuthManager.getConnectedProvidersIds();
                        for (int i = 0; i < activeProviders.size(); i++) {
                            final String provider = activeProviders.get(i);
                            final Response response = socialAuthManager.getProvider(provider).updateStatus(message);

                            handler.post(new Runnable() {
                                @Override
                                public void run() {
                                    int status = response.getStatus();
                                    listener.onExecute(provider, Integer.valueOf(status));
                                }
                            });
                        }
                    } else {
                        final Response response = getCurrentProvider().updateStatus(message);

                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                int status = response.getStatus();
                                listener.onExecute(getCurrentProvider().getProviderId(), Integer.valueOf(status));
                            }
                        });
                    }

                } catch (Exception e) {
                    dialogListener.onError(new SocialAuthError("Message Not Posted", e));
                }
            }
        };

        new Thread(runnable).start();
    }

    /**
     * Method to share message with link preview on facebook only
     *
     * @param message     The message to be send.
     * @param name        title
     * @param caption     subtitle
     * @param description story description
     * @param link        link to be share
     * @param picture     picture for link
     * @throws java.io.UnsupportedEncodingException
     */

    @SuppressWarnings("unchecked")
    public void updateStory(final String message, final String name, final String caption, final String description,
                            final String link, final String picture, final SocialAuthListener<Integer> listener)
            throws UnsupportedEncodingException {

        if (getCurrentProvider().getProviderId().equalsIgnoreCase("facebook")) {
            final Map<String, String> params = new HashMap<String, String>();
            params.put("name", name);
            params.put("caption", caption);
            params.put("description", description);
            params.put("link", link);
            params.put("picture", picture);

            final StringBuilder strb = new StringBuilder();
            strb.append("message=").append(URLEncoder.encode(message, Constants.ENCODING));
            strb.append("&access_token").append("=").append(getCurrentProvider().getAccessGrant().getKey());

            storyResult = strb.toString();
            new StoryTask(listener).execute(params);

        } else {
            Log.d("SocialAuthAdapter", "Provider Not Supported");
        }
    }

    /**
     * Makes HTTP request to a given URL.It attaches access token in URL.
     *
     * @param url          URL to make HTTP request.
     * @param methodType   Method type can be GET, POST or PUT
     * @param params
     * @param headerParams Parameters need to pass as Header Parameters
     * @param body         Request Body
     * @return Response object
     * @throws Exception
     */
    public Response api(final String url, final String methodType, final Map<String, String> params,
                        final Map<String, String> headerParams, final String body) throws Exception {
        Response response = null;
        try {
            response = getCurrentProvider().api(url, methodType, params, headerParams, body);
        } catch (Exception e) {
            throw new SocialAuthException("Error while making request to URL : " + url, e);
        }
        return response;
    }

    /**
     * Synchronous Method to get Profile of User Use this method inside
     * AsyncTask else you will get NetworkOSThreadException
     */

    public Profile getUserProfile() {
        Profile profileList = null;
        try {
            profileList = getCurrentProvider().getUserProfile();
            Log.d("SocialAuthAdapter", "Received Profile Details");
            return profileList;

        } catch (Exception e) {
            Log.d("SocialAuthAdapter", "Profile Details not Received");
            return null;
        }
    }

    /**
     * Asynchronous Method to get User Profile.Returns result in onExecute() of
     * AsyncTaskListener.
     */

    public void getUserProfileAsync(SocialAuthListener<Profile> listener) {
        new ProfileTask(listener).execute();
    }

    /**
     * Synchronous Method to User Contacts. Use this method inside AsyncTask
     * else you will get NetworkOSThreadException
     */

    public List<Contact> getContactList() {
        List<Contact> contactsMap = null;
        try {
            contactsMap = getCurrentProvider().getContactList();
            Log.d("SocialAuthAdapter", "Received Contact list");
            return contactsMap;
        } catch (Exception e) {
            Log.d("SocialAuthAdapter", "Contact list not Received");
            e.printStackTrace();
            return null;
        }
    }

    /**
     * Asynchronous Method to get User Contacts.Returns result in onExecute() of
     * AsyncTaskListener.
     */

    public void getContactListAsync(SocialAuthListener<List<Contact>> listener) {
        new ContactTask(listener).execute();
    }

    /**
     * Synchronous Method to get Feeds of User Use this method inside AsyncTask
     * else you will get NetworkOSThreadException
     */

    public List<Feed> getFeeds() {
        try {
            List<Feed> feedMap = null;
            if (getCurrentProvider().isSupportedPlugin(org.brickred.socialauth.plugin.FeedPlugin.class)) {
                FeedPlugin p = getCurrentProvider().getPlugin(org.brickred.socialauth.plugin.FeedPlugin.class);
                feedMap = p.getFeeds();
                Log.d("SocialAuthAdapter", "Received Feeds");
            } else
                Log.d("SocialAuthAdapter", "Feeds not Supported from Provider");

            return feedMap;

        } catch (Exception e) {
            e.printStackTrace();
            Log.d("SocialAuthAdapter", "Feeds not Available from Provider");
            return null;
        }
    }

    /**
     * Asynchronous Method to get User Feeds.Returns result in onExecute() of
     * AsyncTaskListener.Currently supports Facebook and Twitter
     */

    public void getFeedsAsync(SocialAuthListener<List<Feed>> listener) {
        new FeedTask(listener).execute();
    }

    /**
     * Synchronous Method to get Albums of User Use this method inside AsyncTask
     * else you will get NetworkOSThreadException
     */

    public List<Album> getAlbums() {

        try {
            List<Album> albumMap = null;

            if (getCurrentProvider().isSupportedPlugin(org.brickred.socialauth.plugin.AlbumsPlugin.class)) {
                AlbumsPlugin p = getCurrentProvider().getPlugin(org.brickred.socialauth.plugin.AlbumsPlugin.class);
                albumMap = p.getAlbums();

                Log.d("SocialAuthAdapter", "Received Albums");
            } else
                Log.d("SocialAuthAdapter", "Albums not Supported from Provider");

            return albumMap;
        } catch (Exception e) {
            e.printStackTrace();
            Log.d("SocialAuthAdapter", "Albums not Available from Provider");
            return null;
        }
    }

    /**
     * Asynchronous Method to get User Albums.Returns result in onExecute() of
     * AsyncTaskListener.Currently supports Facebook and Twitter
     */

    public void getAlbumsAsync(SocialAuthListener<List<Album>> listener) {
        new AlbumTask(listener).execute();
    }

    /**
     * Synchronous Method to upload image on provider
     *
     * @param message  message to be attached with image
     * @param fileName image file name
     * @param bitmap   image bitmap to be uploaded
     * @param quality  image quality for jpeg , enter 0 for png
     *                 <p/>
     *                 Returns result in onReceive()
     */
    public Integer uploadImage(String message, String fileName, Bitmap bitmap, int quality) throws Exception {

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        if (fileName.endsWith("PNG") || fileName.endsWith("png")) {
            bitmap.compress(CompressFormat.PNG, 0, bos);
        } else if (fileName.endsWith("JPEG") || fileName.endsWith("JPG") || fileName.endsWith("jpg")
                || fileName.endsWith("jpeg")) {
            bitmap.compress(CompressFormat.JPEG, quality, bos);
        } else {
            throw new SocialAuthException("Image Format not supported");
        }

        InputStream inputStream = new ByteArrayInputStream(bos.toByteArray());

        Response res = null;
        try {
            if (getCurrentProvider().getProviderId().equalsIgnoreCase("facebook")
                    || getCurrentProvider().getProviderId().equalsIgnoreCase("twitter")) {
                res = getCurrentProvider().uploadImage(message, fileName, inputStream);
                Log.d("SocialAuthAdapter", "Image Uploaded");
                return Integer.valueOf(res.getStatus());
            } else {
                throw new SocialAuthException("Provider not Supported");
            }
        } catch (Exception e) {
            throw new SocialAuthException("Image Upload Error");
        }
    }

    /**
     * Asynchronous Method to upload image on provider.Returns result in
     * onExecute() of AsyncTaskListener.Currently supports Facebook and Twitter
     *
     * @param message  message to be attached with image
     * @param fileName image file name
     * @param bitmap   image bitmap to be uploaded
     * @param quality  image quality for jpeg , enter 0 for png
     */

    public void uploadImageAsync(String message, String fileName, Bitmap bitmap, int quality,
                                 SocialAuthListener<Integer> listener) throws Exception {

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        if (fileName.endsWith("PNG") || fileName.endsWith("png")) {
            bitmap.compress(CompressFormat.PNG, 0, bos);
        } else if (fileName.endsWith("JPEG") || fileName.endsWith("JPG") || fileName.endsWith("jpg")
                || fileName.endsWith("jpeg")) {
            bitmap.compress(CompressFormat.JPEG, quality, bos);
        } else {
            throw new SocialAuthException("Image Format not supported");
        }

        InputStream inputStream = new ByteArrayInputStream(bos.toByteArray());
        if (getCurrentProvider().getProviderId().equalsIgnoreCase("facebook")
                || getCurrentProvider().getProviderId().equalsIgnoreCase("twitter")) {
            new UploadImageTask(listener).execute(message, fileName, inputStream);
        } else {
            throw new SocialAuthException("Provider not Supported");
        }
    }

    public void getCareerAsync(SocialAuthListener<Career> listener) {
        new CareerTask(listener).execute();
    }

    /**
     * Enum of all supported providers containing provider name , callback url
     * and cancel url
     */
    public enum Provider {
        FACEBOOK(Constants.FACEBOOK, "fbconnect://success", "fbconnect://success?error_reason"), TWITTER(
                Constants.TWITTER, "http://socialauth.in/socialauthdemo/socialAuthSuccessAction.do",
                "http://socialauth.in/socialauthdemo/socialAuthSuccessAction.do?denied"), LINKEDIN(Constants.LINKEDIN,
                "http://socialauth.in/socialauthdemo/socialAuthSuccessAction.do",
                "http://socialauth.in/socialauthdemo/socialAuthSuccessAction.do?oauth_problem"), MYSPACE(
                Constants.MYSPACE, "http://socialauth.in", "http://socialauth.in/?oauth_problem"), RUNKEEPER(
                Constants.RUNKEEPER, "http://socialauth.in/socialauthdemo/socialauthSuccessAction.do",
                "http://socialauth.in/socialauthdemo/socialauthSuccessAction.do/?error"), YAHOO(Constants.YAHOO,
                "http://socialauth.in/socialauthdemo", "http://socialauth.in/socialauthdemo/?oauth_problem"), FOURSQUARE(
                Constants.FOURSQUARE, "http://socialauth.in/socialauthdemo/socialAuthSuccessAction.do",
                "http://socialauth.in/socialauthdemo/socialAuthSuccessAction.do/?oauth_problem"), GOOGLE(
                Constants.GOOGLE, "http://socialauth.in/socialauthdemo",
                "http://socialauth.in/socialauthdemo/?oauth_problem"), YAMMER(Constants.YAMMER,
                "http://socialauth.in/socialauthdemo/socialAuthSuccessAction.do",
                "http://socialauth.in/socialauthdemo/socialAuthSuccessAction.do/?oauth_problem"), SALESFORCE(
                Constants.SALESFORCE, "https://socialauth.in:8443/socialauthdemo/socialAuthSuccessAction.do",
                "http://socialauth.in/socialauthdemo/socialAuthSuccessAction.do/?oauth_problem"), GOOGLEPLUS(
                Constants.GOOGLE_PLUS, "http://opensource.brickred.com/socialauthdemo/socialAuthSuccessAction.do",
                "http://opensource.brickred.com/socialauthdemo/socialAuthSuccessAction.do/?oauth_problem"), INSTAGRAM(
                Constants.INSTAGRAM, "http://opensource.brickred.com/socialauthdemo/socialAuthSuccessAction.do",
                "http://opensource.brickred.com/socialauthdemo/socialAuthSuccessAction.do/?oauth_problem"), FLICKR(
                Constants.FLICKR, "http://socialauth.in/socialauthdemo/socialAuthSuccessAction.do",
                "http://socialauth.in/socialauthdemo/socialAuthSuccessAction.do/?oauth_problem"), EMAIL(SHARE_MAIL, "",
                ""), MMS(SHARE_MMS, "", ""), GENERIC("", "", "");

        private String name;
        private String cancelUri;
        private String callbackUri;

        /**
         * Constructor with unique string representing the provider
         *
         * @param name
         */
        Provider(String name, String callbackUri, String cancelUri) {
            this.name = name;
            this.cancelUri = cancelUri;
            this.callbackUri = callbackUri;
        }

        /**
         * returns cancel URI
         */
        String getCancelUri() {
            return this.cancelUri;
        }

        /**
         * returns Callback URI
         */
        String getCallBackUri() {
            return this.callbackUri;
        }

        /**
         * Set callback URI
         */
        public void setCallBackUri(String callbackUri) {
            this.callbackUri = callbackUri;
        }

        /**
         * Set Name
         */
        public void setName(String name) {
            this.name = name;
        }

        /**
         * Returns the unique string representing the provider
         */
        @Override
        public String toString() {
            return name;
        }
    }

    /**
     * AsyncTask to uploadImage
     */

    private class StoryTask extends AsyncTask<Map<String, String>, Void, Integer> {

        SocialAuthListener<Integer> listener;

        private StoryTask(SocialAuthListener<Integer> listener) {
            this.listener = listener;
        }

        @Override
        protected Integer doInBackground(Map<String, String>... params) {
            // Call using API method of socialauth
            Response response = null;
            Map<String, String> paramsMap = params[0];
            try {
                response = getCurrentProvider().api(UPDATE_STATUS_URL, MethodType.POST.toString(), paramsMap, null,
                        storyResult);
                Log.d("Status", String.valueOf(response.getStatus()));
                return Integer.valueOf(response.getStatus());
            } catch (Exception e) {
                e.printStackTrace();
                dialogListener.onError(new SocialAuthError("Message Not Posted", e));
                return null;
            }

        }

        @Override
        protected void onPostExecute(Integer status) {
            listener.onExecute(getCurrentProvider().getProviderId(), status);
        }
    }

    /**
     * AsyncTask to get user profile
     */

    private class ProfileTask extends AsyncTask<Void, Void, Profile> {

        SocialAuthListener<Profile> listener;

        private ProfileTask(SocialAuthListener<Profile> listener) {
            this.listener = listener;
        }

        @Override
        protected Profile doInBackground(Void... params) {

            Profile profileList = null;
            try {
                profileList = getCurrentProvider().getUserProfile();
                Log.d("SocialAuthAdapter", "Received Profile Details");
                return profileList;

            } catch (Exception e) {
                e.printStackTrace();
                listener.onError(new SocialAuthError("Profile Details not Received", e));
                return null;
            }
        }

        @Override
        protected void onPostExecute(Profile profile) {
            listener.onExecute(getCurrentProvider().getProviderId(), profile);
        }
    }

    /**
     * AsyncTask to retrieve contacts
     */

    private class ContactTask extends AsyncTask<Void, Void, List<Contact>> {

        SocialAuthListener<List<Contact>> listener;

        private ContactTask(SocialAuthListener<List<Contact>> listener) {
            this.listener = listener;
        }

        @Override
        protected List<Contact> doInBackground(Void... params) {
            List<Contact> contactsMap = null;
            try {
                contactsMap = getCurrentProvider().getContactList();
                Log.d("SocialAuthAdapter", "Received Contact list");
                return contactsMap;
            } catch (Exception e) {
                e.printStackTrace();
                listener.onError(new SocialAuthError("Contact List not Received", e));
                return null;
            }
        }

        @Override
        protected void onPostExecute(List<Contact> contactsMap) {
            listener.onExecute(getCurrentProvider().getProviderId(), contactsMap);
        }
    }

    /**
     * AsyncTask to retrieve feeds
     */
    private class FeedTask extends AsyncTask<Void, Void, List<Feed>> {

        SocialAuthListener<List<Feed>> listener;

        private FeedTask(SocialAuthListener<List<Feed>> listener) {
            this.listener = listener;
        }

        @Override
        protected List<Feed> doInBackground(Void... params) {

            try {
                List<Feed> feedMap = null;
                if (getCurrentProvider().isSupportedPlugin(org.brickred.socialauth.plugin.FeedPlugin.class)) {
                    FeedPlugin p = getCurrentProvider().getPlugin(org.brickred.socialauth.plugin.FeedPlugin.class);
                    feedMap = p.getFeeds();
                    Log.d("SocialAuthAdapter", "Received Feeds");
                } else
                    Log.d("SocialAuthAdapter", "Feeds not Supported from Provider");

                return feedMap;

            } catch (Exception e) {
                e.printStackTrace();
                listener.onError(new SocialAuthError("Feed not Available from Provider", e));
                return null;
            }
        }

        @Override
        protected void onPostExecute(List<Feed> feedMap) {
            listener.onExecute(getCurrentProvider().getProviderId(), feedMap);
        }
    }

    /**
     * AsyncTask to retrieve albums
     */

    private class AlbumTask extends AsyncTask<Void, Void, List<Album>> {

        SocialAuthListener<List<Album>> listener;

        private AlbumTask(SocialAuthListener<List<Album>> listener) {
            this.listener = listener;
        }

        @Override
        protected List<Album> doInBackground(Void... params) {
            try {
                List<Album> albumMap = null;

                if (getCurrentProvider().isSupportedPlugin(org.brickred.socialauth.plugin.AlbumsPlugin.class)) {
                    AlbumsPlugin p = getCurrentProvider().getPlugin(org.brickred.socialauth.plugin.AlbumsPlugin.class);
                    albumMap = p.getAlbums();

                    Log.d("SocialAuthAdapter", "Received Albums");
                } else
                    Log.d("SocialAuthAdapter", "Albums not Supported from Provider");

                return albumMap;
            } catch (Exception e) {
                e.printStackTrace();
                listener.onError(new SocialAuthError("Albums not Available from Provider", e));
                return null;
            }
        }

        @Override
        protected void onPostExecute(List<Album> albumMap) {

            listener.onExecute(getCurrentProvider().getProviderId(), albumMap);
        }
    }

    /**
     * AsyncTask to uploadImage
     */

    private class UploadImageTask extends AsyncTask<Object, Void, Integer> {

        SocialAuthListener<Integer> listener;

        private UploadImageTask(SocialAuthListener<Integer> listener) {
            this.listener = listener;
        }

        @Override
        protected Integer doInBackground(Object... params) {
            Response res = null;
            try {
                res = getCurrentProvider().uploadImage((String) params[0], (String) params[1], (InputStream) params[2]);
                Log.d("SocialAuthAdapter", "Image Uploaded");
                return Integer.valueOf(res.getStatus());
            } catch (Exception e) {
                listener.onError(new SocialAuthError("Image Upload Error", e));
                return null;
            }
        }

        @Override
        protected void onPostExecute(Integer status) {

            listener.onExecute(getCurrentProvider().getProviderId(), status);
        }
    }

    /**
     * AsyncTask to uploadImage
     */

    private class CareerTask extends AsyncTask<Void, Void, Career> {

        SocialAuthListener<Career> listener;

        private CareerTask(SocialAuthListener<Career> listener) {
            this.listener = listener;
        }

        @Override
        protected Career doInBackground(Void... params) {
            try {
                Career careerList = null;

                if (getCurrentProvider().isSupportedPlugin(org.brickred.socialauth.plugin.CareerPlugin.class)) {
                    CareerPlugin p = getCurrentProvider().getPlugin(org.brickred.socialauth.plugin.CareerPlugin.class);
                    careerList = p.getCareerDetails();
                    Log.d("SocialAuthAdapter", "Received Career Details");

                } else
                    Log.d("SocialAuthAdapter", "Career Details only Supported from Linkedin");

                return careerList;
            } catch (Exception e) {
                e.printStackTrace();
                listener.onError(new SocialAuthError("Career Details not Available from Provider", e));
                return null;
            }
        }

        @Override
        protected void onPostExecute(Career careerList) {

            listener.onExecute(getCurrentProvider().getProviderId(), careerList);
        }
    }

}
